package com.xbongbong.paas.toolbox.util;

import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.toolbox.wrap.Html2pdfForEsignVO;
import com.xbongbong.paas.toolbox.wrap.PdfParamDTO;
import com.xbongbong.saas.constant.BasicConstant;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Objects;
import java.util.Random;

/**
 * 网页url转pdf工具类
 *         pdf中文乱码参考：https://blog.51cto.com/aiilive/1340243
 *         参考实例：https://www.cnblogs.com/xiangxiong/p/7645679.html
 *                  http://daveztong.github.io/2016/12/06/wkhtmltopdf%E6%8A%98%E8%85%BE%E8%AE%B0/
 *         linux安装插件教程：https://www.jianshu.com/p/328e203a51a6
 *         服务器需安装依赖：https://github.com/wkhtmltopdf/packaging/blob/0.12/docker/Dockerfile.centos64
 * @author zheng.li
 * @version v1.0
 * @date 2019/4/17 10:35
 * @since v1.0
 */
public class Wkhtmltopdf {

    /**
     * 日志
     */
   static final Logger logger = LoggerFactory.getLogger(Wkhtmltopdf.class);
    /**
     *  三联单标记
     */
   private static final String DIVISION = "division";
    /**
     * 风景模式（页面横向）
     */
    private static final String LANDSCAPE = "Landscape";
    /**
     * 肖像模式（页面竖向）
     */
    private static final String PORTRAIT = "Portrait";
    /**
     * 页面横向
     */
    private static final String HORIZONTAL = "horizontal";
    /**
     * 自定义纸张
     */
    private static final String CUSTOM = "Custom";
    /**
     * 打印临时文件保存目录
     */
    public static final String PRINT_BASE_PATH = "/acs/upload/print/";

    private static Random random = new Random();

    /**
     * html转pdf(e签宝使用)
     * @param srcUrl  html文件路径
     * @param pdfParamDTO pdf参数配置
     * @return pdf文件
     */
    public static Html2pdfForEsignVO html2pdfForEsign(String srcUrl, PdfParamDTO pdfParamDTO, HashMap<String,String > nameToFileName) {

        String filename = System.currentTimeMillis() + random.nextInt(1000) + ".pdf";
        byte[] bytes = html2pdf(srcUrl, nameToFileName, pdfParamDTO, filename);
        Html2pdfForEsignVO html2pdfForEsignVO = new Html2pdfForEsignVO();
        html2pdfForEsignVO.setBytes(bytes);
        html2pdfForEsignVO.setFileName(filename);
        return html2pdfForEsignVO;
    }

    /**
     * 调用这个方法,别调用底层方法
     * html转pdf
     * @param srcUrl html文件路径
     * @param nameToFileName  页眉页脚html文件路径
     * @param pdfParamDTO pdf参数配置
     * @return
     */
    public static byte[] html2pdf(String srcUrl,HashMap<String,String> nameToFileName, PdfParamDTO pdfParamDTO, String fileName) {
        if (Objects.isNull(fileName)) {
            fileName = System.currentTimeMillis() + random.nextInt(1000) + ".pdf";
        }
        return html2pdf(srcUrl,nameToFileName, PRINT_BASE_PATH + fileName, pdfParamDTO);
    }


    /**
     * 转化方法别调用,调用上层的方法
     * html 转pdf
     * @param srcUrl html 文件路径
     * @param nameToFileName    页眉页脚对应的html
     * @param dest  生成pdf路径
     * @param pdfParamDTO pdf 参数配置
     * @return
     */
    private static byte[] html2pdf(String srcUrl, HashMap<String, String> nameToFileName, String dest, PdfParamDTO pdfParamDTO) {
        String space = " ";
        String wkhtmltopdf = findExecutable();
        if (StringUtil.isEmpty(wkhtmltopdf)) {
            logger.error("no wkhtmltopdf found!");
            throw new RuntimeException("html转换pdf出错了");
        }
        File file = new File(dest);
        File parent = file.getParentFile();
        if (!parent.exists()) {
            boolean dirsCreation = parent.mkdirs();
            logger.info("create dir for new file,{}", dirsCreation);
        }
        if (pdfParamDTO == null) {
            pdfParamDTO = new PdfParamDTO();
        }
        // 边距
        JSONObject padding = pdfParamDTO.getPadding();
        // 页面尺寸
        String size = pdfParamDTO.getSize() == null ? "A4" : pdfParamDTO.getSize();
        // 自定义页面宽度
        Integer width = pdfParamDTO.getWidth();
        // 自定义页面高度
        Integer height = pdfParamDTO.getHeight();
        // 顶部边距
        Double top = 10.0;
        // 底部边距
        Double bottom = 10.0;
        // 页眉
        JSONObject footer = pdfParamDTO.getFooter();
        // 页脚
        JSONObject header = pdfParamDTO.getHeader();
        // 页码
        JSONObject page = pdfParamDTO.getPage();
        if (padding != null) {
            top = StringUtil.toDouble(padding.getString("top"), 10.0);
            bottom = StringUtil.toDouble(padding.getString("bottom"), 10.0);
        }
        // 模式
        String orientation = PORTRAIT;
        if (size.contains(DIVISION)) {
            // 三联单
//            if (size.contains("second-division")) {
//                width = 241;
//                height = 140;
//            } else if (size.contains("third-division")) {
//                width = 241;
//                height = 93;
//            }
        }
        if (size.contains(HORIZONTAL)) {
            // 横向 b
            orientation = LANDSCAPE;
        }
        // 自定义纸张
        if (size.contains(CUSTOM)) {
            orientation = null;
            top = 0.0;
            bottom = 0.0;
        }
        size = size.replaceAll("-horizontal", "").replaceAll("-vertical", "");
        // 执行命令
        StringBuilder cmd = new StringBuilder();
        // 页面大小信息设置
        StringBuilder pageSizeBuilder = new StringBuilder();
        if (width != null && height != null) {
            pageSizeBuilder.append("--page-width").append(space).append(width).append(space)
                    .append("--page-height").append(space).append(height).append(space);
        } else {
            pageSizeBuilder.append("--page-size").append(space).append(size).append(space);
        }
        //页眉页脚
        StringBuilder pageFooter = new StringBuilder();
        StringBuilder pageHeader = new StringBuilder();
        StringBuilder pageNum = new StringBuilder();
        // 处理页脚
        // BasicConstant.THREE 主要调试了很多次发现的页眉html和正文会有部分重合,3刚刚合适
        if (Objects.nonNull(nameToFileName.get("footer")) && Objects.equals(footer.getInteger("enable"), BasicConstant.ONE)) {
            pageFooter.append("--footer-html").append(space).append(nameToFileName.get("footer")).append(space)
                    .append("--footer-spacing").append(space).append(3).append(space);
            bottom = bottom > footer.getDouble("footerHeight") + BasicConstant.THREE  ? bottom : footer.getDouble("footerHeight") + BasicConstant.THREE;
        }
        // 处理页眉
        if (Objects.nonNull(nameToFileName.get("header")) && Objects.equals(header.getInteger("enable"), BasicConstant.ONE)) {
            pageHeader.append("--header-html").append(space).append(nameToFileName.get("header")).append(space)
                    .append("--header-spacing").append(space).append(3).append(space);
            top = top > header.getDouble("headerHeight") + BasicConstant.THREE ? top : header.getDouble("headerHeight") + BasicConstant.THREE;
        }

        // 处理页码
        // 处理页眉页脚未开启 ,页码开启
        if (Objects.nonNull(page) && Objects.equals(page.getInteger("enable"), BasicConstant.ONE)) {
            if (Objects.equals(page.getInteger("location"), BasicConstant.ONE)) {
                if (Objects.nonNull(nameToFileName.get("footer")) && Objects.equals(footer.getInteger("enable"), BasicConstant.ONE) ) {
                    pageFooter.append("--footer-html").append(space).append(nameToFileName.get("footer")).append(space).append("--footer-spacing").append(space).append(3).append(space);
                    bottom = bottom > page.getDouble("pageHeight") + BasicConstant.THREE ? bottom : page.getDouble("pageHeight") + BasicConstant.THREE;
                }
            } else if (Objects.equals(page.getInteger("location"), BasicConstant.ZERO)) {
                if (Objects.nonNull(nameToFileName.get("header")) && Objects.equals(header.getInteger("enable"), BasicConstant.ONE)) {
                    pageHeader.append("--header-html").append(space).append(nameToFileName.get("header")).append(space)
                            .append("--header-spacing").append(space).append(3).append(space);
                    top = top > page.getDouble("pageHeight") + BasicConstant.THREE ? top : page.getDouble("pageHeight") + BasicConstant.THREE;
                }
            }
        }
        cmd.append(findExecutable()).append(space)
                .append("-d").append(space).append("96").append(space);
                if (Objects.nonNull(orientation)) {
                    cmd.append("-O").append(space).append(orientation).append(space);
                }
        cmd.append("--margin-top").append(space).append(top).append(space)
                .append("--margin-bottom").append(space).append(bottom).append(space)
                .append("--margin-left").append(space).append(0).append(space)
                .append("--margin-right").append(space).append(0).append(space)
                .append(pageSizeBuilder)
                .append(pageFooter)
                .append(pageHeader)
                .append(pageNum)
                .append(srcUrl).append(space)
                //.append("--footer-right").append(space).append("页码:[page]").append(space)
                //.append("--footer-font-size").append(space).append("5").append(space)
                //.append("--footer-left").append(space).append("打印时间:[date]").append(space)
                .append("--disable-smart-shrinking").append(space)
                .append("--debug-javascript").append(space)
                .append("--load-media-error-handling").append(space).append("ignore").append(space)
                .append("--load-error-handling").append(space).append("ignore").append(space)
                .append(space)
                // Page Options
                .append("--print-media-type").append(space)
                .append(dest);
        InputStream is = null;
        try {
            String finalCmd = cmd.toString();
            logger.info("final cmd:{}", finalCmd);
            Process proc = Runtime.getRuntime().exec(finalCmd);
            new Thread(new ProcessStreamHandler(proc.getInputStream())).start();
            new Thread(new ProcessStreamHandler(proc.getErrorStream())).start();
            proc.waitFor();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            is = new FileInputStream(file);
            byte[] buf = new byte[1024];
            while (is.read(buf, 0, buf.length) != -1) {
                baos.write(buf, 0, buf.length);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            logger.error("html转换pdf出错", e);
            throw new RuntimeException("html转换pdf出错了");
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 返回系统中wkhtmltopdf执行程序的系统路径
     *
     * @return the wkhtmltopdf command according to the OS
     */
    private static String findExecutable() {
        Process p;
        try {
            String osname = System.getProperty("os.name").toLowerCase();
            String cmd = osname.contains("windows") ? "where wkhtmltopdf" : "which wkhtmltopdf";
            p = Runtime.getRuntime().exec(cmd);
            new Thread(new ProcessStreamHandler(p.getErrorStream())).start();
            p.waitFor();
            return IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
        } catch (Exception e) {
            logger.error("no wkhtmltopdf found!", e);
        }
        return "";
    }

    private static class ProcessStreamHandler implements Runnable {
        private InputStream is;
        private ProcessStreamHandler(InputStream is) {
            this.is = is;
        }
        @Override
        public void run() {
            BufferedReader reader = null;
            try {
                InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
                reader = new BufferedReader(isr);
                String line;
                while ((line = reader.readLine()) != null) {
                    // 输出内容
                    logger.info(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    // public static  void main (String[] args){
    //     String srcUrl = "http://daveztong.github.io/2016/12/06/wkhtmltopdf%E6%8A%98%E8%85%BE%E8%AE%B0/";
    //     byte[] bytes = html2pdf(srcUrl);
    //     System.out.println(Arrays.toString(bytes));
    // }

}
